package com.example.concurrent.chapter12;

import java.util.concurrent.TimeUnit;

/**
 * @author Liuqz
 * @date 2018/11/25 15:13
 *
 * 被volatile关键字修饰的实例变量或者类变量具备如下两种语义:
 * 1.保证了不同线程之间对共享变量操作时的可见性,也就是说当一个线程修改了volatile修饰的变量,另一个线程会立即看到最新的值
 * 2.禁止对指令进行重排序操作
 */
public class VolatileFoo {

    //init_value的最大值
    final static int MAX = 5;

    //init_value的初始值
    static volatile int init_value = 0;

    /**
     * volatile变量规则 : 如果一个变量使用volatile关键字修饰,一个线程对它进行读操作,一个线程对它进行写操作,那么写入操作肯定会先发生于读操作
     * 传递规则 : 如果操作A先于B,而操作B又先于C ,则可以得出操作A肯定要先于操作C
     * 线程启动规则 : Thread对象的start()方法先行发生于对该线程的任何动作,只有start之后线程才能真正运行,否则Thread只是一个对象而已
     * 线程中断规则 : 对线程运行interrupt()方法肯定要优先于捕获到中断信号,这句话的意思是指如果线程收到了中断信号,那么在此之前势必要有interrupt()
     * 线程终结规则 : 线程中的所有操作都要先行发生于线程的终止检测,通俗地讲,线程的任务执行,逻辑单元的执行肯定要发生于线程死亡之前
     * 对象的终结规则 : 一个对象初始化的完成先行发生于finalize()方法之前
     * @param args
     */

    public static void main(String[] args) {
        //启动一个Reader线程,当发现local_value和init_value不同时,则输出init_value被修改的信息
        new Thread(() -> {
            int localValue = init_value;
            while (localValue < MAX) {
                if (init_value != localValue) {
                    System.out.printf("The init_value is updated to [%d]\n", init_value);

                    //对localValue进行重新赋值
                    localValue = init_value;
                }
            }
        },"Reader").start();

        //启动Updater线程,主要用于对init_value的修改,当local_value>=5的时候则退出生命周期
        new Thread(() -> {
            int localValue = init_value;
            while (localValue < MAX) {
                //修改init_value
                System.out.printf("The init_value will be changed to [%d]\n", ++localValue);
                init_value = localValue;

                try {
                    //短暂休眠,目的是为了使Reader线程能够及时输出变化内容
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"Updater").start();
    }

    /**
     * 当init_value没有被volatile修饰时
     * result : The init_value will be changed to [1]
     *          The init_value will be changed to [2]
     *          The init_value will be changed to [3]
     *          The init_value will be changed to [4]
     *          The init_value will be changed to [5]
     *
     * 当init_value被volatile修饰时
     * result : The init_value will be changed to [1]
     *          The init_value is updated to [1]
     *          The init_value will be changed to [2]
     *          The init_value is updated to [2]
     *          The init_value will be changed to [3]
     *          The init_value is updated to [3]
     *          The init_value will be changed to [4]
     *          The init_value is updated to [4]
     *          The init_value will be changed to [5]
     *          The init_value is updated to [5]
     *
     * volatile关键字只能修饰类变量和实例变量,对于方法参数,局部变量以及实例常量,类常量都不能被修饰
     * 比如该类中的MAX就不能被修饰
     *
     *
     * Java内存模型决定了一个线程对共享变量的写入何时对其他线程可见,Java内存模型定义了线程和主内存之间的抽象关系,具体如下:
     * 1.共享变量存储与主内存之中,每个线程都可以访问
     * 2.每个线程都有私有的工作内存或者称为本地内存
     * 3.工作内存只存储该线程对共享变量的副本
     * 4.线程不能直接操作主内存,只有先操作了工作内存之后才能写入主内存
     * 5.工作内存和Java内存模型一样也是一个抽象的概念,它其实并不存在,它涵盖了缓存器,寄存器,编译器优化以及硬件等
     */
}
